Imports System.Net
Imports System.Runtime.InteropServices

Namespace Socket

    Module mSocket

        Public WithEvents SockServer As EvoLib.SocketServer.SocketServer

        Public Function StartServer(ByVal IP As IPAddress, ByVal iPort As Integer) As Boolean

            SockServer = New SocketServer.SocketServer()

            'AddHandler SockServer.onDataArrival, New EvoLib.SocketServer.SocketServer.onDataArrivalEventHandler(AddressOf SockServer_onDataArrival)
            'AddHandler SockServer.onAccept, New EvoLib.SocketServer.SocketServer.onAcceptEventHandler(AddressOf SockServer_onAccept)
            'AddHandler SockServer.onDisconnect, New EvoLib.SocketServer.SocketServer.onDisconnectEventHandler(AddressOf SockServer_onDisconnect)
            'AddHandler SockServer.onError, New EvoLib.SocketServer.SocketServer.onErrorEventHandler(AddressOf SockServer_onError)

            Return SockServer.Listen(IP, iPort)

            'Socket.SockServer.EndConnection(New Connection().Index)

        End Function

        Public Sub Shutdown()

            If Not SockServer Is Nothing Then
                SockServer = Nothing
            End If

        End Sub

        Private Sub SockServer_onAccept(ByRef Conn As Connection) Handles SockServer.onAccept

            'TODO: Remove sound on connection accept
            'Beep()
            'Beep()
            'Beep()




            Dim p As Packet
            'maintainance mode
            'If Globals.bUnderMaintainance Then

            '    p.Username = DefaultUsername
            '    p.PacketType = GenericPacketType.ServerState
            '    p.PacketOpCode = PacketOpCode.NegAcknowledge

            '    Dim msg As GenericMessage
            '    msg.MESSAGE = "The server is currently in Maintainance mode! GM Login Only"

            '    Socket.SockServer.SendData(Protocol.MakePacket(p, msg), Conn.Index)

            '    Threading.Thread.Sleep(1500)

            'End If


            Conn.Crypto = Cryptography.DefaultCrypto

            p.Username = DefaultUsername
            p.PacketType = LoginPacketType.TryLogin
            p.PacketOpCode = PacketOpCode.Notify
            Logger.LoggerWriteLine("Connection from IP [" & Conn.IP & "] accepted")
            SockServer.SendData(Protocol.MakePacket(p, Nothing), Conn.Index)

        End Sub

        Private Sub SockServer_onDataArrival(ByVal Data() As Byte, ByRef Conn As Connection) Handles SockServer.onDataArrival

            Conn.LastRecievedDataAt = DateTime.Now

            Dim bRecievedBytes() As Byte
            Dim bIncompleteData() As Byte = SockServer.ConnectionList.GetIncompleteData(Conn.Index)

            If Not bIncompleteData Is Nothing Then
                ReDim bRecievedBytes(Data.Length + bIncompleteData.Length - 1)
                Array.Copy(bIncompleteData, bRecievedBytes, bIncompleteData.Length)
                Array.Copy(Data, 0, bRecievedBytes, bIncompleteData.Length, Data.Length)
                SockServer.ConnectionList.SetIncompleteData(Conn.Index, Nothing)
            Else
                ReDim bRecievedBytes(Data.Length - 1)
                Array.Copy(Data, bRecievedBytes, Data.Length)
            End If

            Dim info As New Packet
            Dim struct() As Byte = Nothing

            Dim ret As ParsePacketCode

            Try

                If Conn.Crypto Is Nothing Then
                    ret = ParsePacket(bRecievedBytes, info, struct, Cryptography.DefaultCrypto)
                Else
                    ret = ParsePacket(bRecievedBytes, info, struct, Conn.Crypto)
                End If

                If ret = ParsePacketCode.NoData Then
                    Exit Sub
                ElseIf ret = ParsePacketCode.PacketError Then
                    Debug.Print("LoginSvr.mSocket.SockServer_onDataArrival: ParsePacket returned PacketError")
                    If Not bRecievedBytes Is Nothing Then
                        ReDim bIncompleteData(bRecievedBytes.Length - 1)
                        Marshal.Copy(bRecievedBytes, 0, Marshal.AllocHGlobal(bIncompleteData(0)), bRecievedBytes.Length)
                        SockServer.ConnectionList.SetIncompleteData(Conn.Index, bIncompleteData)
                    End If

                ElseIf ret = ParsePacketCode.Incomplete Then
                    SockServer.ConnectionList.SetIncompleteData(Conn.Index, bRecievedBytes)
                    If Not bRecievedBytes Is Nothing Then
                        ReDim bIncompleteData(bRecievedBytes.Length - 1)
                        Marshal.Copy(bRecievedBytes, 0, Marshal.AllocHGlobal(bIncompleteData(0)), bRecievedBytes.Length)
                        SockServer.ConnectionList.SetIncompleteData(Conn.Index, bIncompleteData)
                    End If

                ElseIf ret = ParsePacketCode.DecryptError Then

                    Output.AddLine("\B0\F7CR>\B8\F3 Decrypt error\F0 [ConnectionID:" & Conn.Index & " LoginID:" & Conn.LoginID & "(\B3\F4" & Conn.Username & "\B8\F0))]")
                    Packets.SendInvalidPwdPacket(Conn)
                    
                ElseIf ret = ParsePacketCode.OK Then

                    'START DEBUG OUTPUT CODE

                    If Globals.bDebugMode And info.PacketType <> GenericPacketType.PingTest Then

                        Dim txt As String = "===[SERVER SOCKET(ConnectionID:" & Conn.Index & " LoginID:" & Conn.LoginID & "(" & Conn.Username & ")) > DATA IN]====================================================" & vbCrLf
                        txt &= "     UNAME: " & info.Username & vbCrLf
                        'NOTE: This line is server based and must be changed for correct output
                        'txt &= "    PACKET: " & CType(info.PacketType, GamePacketType).ToString & vbCrLf
                        ' 
                        If System.Enum.IsDefined(GetType(GamePacketType), info.PacketType) Then
                            txt &= "      TYPE: Game" & vbCrLf
                            txt &= "    PACKET: " & System.Enum.GetName(GetType(GamePacketType), info.PacketType) & vbCrLf
                        ElseIf System.Enum.IsDefined(GetType(LoginPacketType), info.PacketType) Then
                            txt &= "      TYPE: Login" & vbCrLf
                            txt &= "    PACKET: " & System.Enum.GetName(GetType(LoginPacketType), info.PacketType) & vbCrLf
                        ElseIf System.Enum.IsDefined(GetType(GenericPacketType), info.PacketType) Then
                            txt &= "      TYPE: Generic" & vbCrLf
                            txt &= "    PACKET: " & System.Enum.GetName(GetType(GenericPacketType), info.PacketType) & vbCrLf
                        End If

                        txt &= "    OPCODE: " & info.PacketOpCode.ToString & vbCrLf & vbCrLf

                        'If Not struct Is Nothing Then
                        '    txt &= "STRUCT BYTES:" & vbCrLf

                        '    Dim tmp As String
                        '    Dim asciitxt As String = ""
                        '    Dim i As Integer
                        '    Dim c As Integer
                        '    Dim tmpbyte(0) As Byte

                        '    For i = 0 To struct.Length - 1

                        '        tmp = struct(i)
                        '        Do Until tmp.Length = 3
                        '            tmp = " " & tmp
                        '        Loop

                        '        If tmp = 0 Then
                        '            txt &= "  . "
                        '        Else
                        '            txt &= " " & tmp
                        '        End If

                        '        If struct(i) < 32 Then
                        '            asciitxt &= "."
                        '        Else
                        '            tmpbyte(0) = struct(i)
                        '            asciitxt &= System.Text.Encoding.ASCII.GetString(tmpbyte)
                        '        End If

                        '        c += 1
                        '        If c = 20 Then
                        '            c = 0
                        '            txt &= "  " & asciitxt & vbCrLf
                        '            asciitxt = ""
                        '        ElseIf c = 10 Then
                        '            asciitxt &= "   "
                        '            txt &= "    "
                        '        End If

                        '    Next

                        '    If c < 10 Then txt = txt & "    "
                        '    If c > 0 Then txt &= "  " & Space((20 - c) * 4) & asciitxt & vbCrLf

                        'txt &= struct.Length & " bytes" & vbCrLf & vbCrLf

                        'End If

                        Debug.Print(txt)

                    End If

                    'STOP DEBUG OUTPUT CODE

                    Packets.PacketIn(info, struct, Conn)

                    If Not bRecievedBytes Is Nothing Then
                        Call SockServer_onDataArrival(bRecievedBytes, Conn)
                    End If

                Else
                    Debug.Print("LoginSvr.mSocket.SockServer_onDataArrival: ParsePacket returned an invalid value")
                End If

            Catch ex As Exception
                Debug.Print("Socket Error in onDataArrival Event - " & ex.ToString())

                Exit Sub
            End Try

        End Sub

        Private Sub SockServer_onDisconnect(ByVal Conn As SocketServer.mSocketServer.Connection, ByVal IP As String) Handles SockServer.onDisconnect

            If Conn.CharacterID > 0 Then

                Dim map As MirMap = GetMapFromConn(Conn)
                If Not map Is Nothing Then
                    RemovePlayer(Conn, map)
                End If

                Packets.UpdateUserList_Offline(Conn.CharacterID)

            End If

        End Sub

        Private Sub SockServer_onError(ByVal ErrorType As String, ByVal Err As System.Exception, ByVal Conn As SocketServer.mSocketServer.Connection) Handles SockServer.onError

            Try

                Debug.WriteLine("Sock Error : " & Err.ToString)

            Catch
            End Try

        End Sub

        Public Sub CleanupInactiveSockets()

            Socket.SockServer.ConnectionList.Cleanup()

        End Sub

    End Module

End Namespace